As we all know, Democratic Party and Republican Party are two major contemporary political parties in the United States, the former is more progressive and the latter is more conservative. The two parties differ in their policies but also have similarities.

As American people vote to decide next president, they are basically choosing parties, in most cases, between Democratic and Republican. So one question we could ask is: what are the differences and similarities of the president inauguration speeches from different parties?

In this project, I will analyze inauguration speeches of different presidents from Democratic and Republican, trying to find differences and similarities. Here is some questions we want to find the answer:

Step 0: Install and load libraries

packages.used=c("rvest", "qdap",  "tidytext","plyr","syuzhet",
                "sentimentr", "gplots", "dplyr","tidyr","gridExtra",
                "tm","topicmodels","ggplot2","wordcloud","RColorBrewer")
# check packages that need to be installed.
packages.needed=setdiff(packages.used, 
                        intersect(installed.packages()[,1], 
                                  packages.used))
# install additional packages
if(length(packages.needed)>0){
  install.packages(packages.needed, dependencies = TRUE)
}
lapply(packages.used, require, character.only = TRUE)
source("../lib/plotstacked.R")
source("../lib/speechFuncs.R")

This notebook was prepared with the following environmental settings.

print(R.version)
               _                           
platform       x86_64-apple-darwin13.4.0   
arch           x86_64                      
os             darwin13.4.0                
system         x86_64, darwin13.4.0        
status                                     
major          3                           
minor          3.3                         
year           2017                        
month          03                          
day            06                          
svn rev        72310                       
language       R                           
version.string R version 3.3.3 (2017-03-06)
nickname       Another Canoe               

Step 1: Import data

In this project, we need information about president inauguration speeches. We have basic information in ‘InaugurationInfo.csv’ and we scraped speech URLs from http://www.presidency.ucsb.edu/inaugurals.php.

# basic information about inauguration speech
speeches <- read.csv("../data/InaugurationInfo.csv")
# inauguaral speeches
main.page <- read_html(x = "http://www.presidency.ucsb.edu/inaugurals.php")
# Get link URLs
# f.speechlinks is a function for extracting links from the list of speeches. 
inaug=f.speechlinks(main.page)
#head(inaug)
#as.Date(inaug[,1], format="%B %e, %Y")
inaug=inaug[-nrow(inaug),] # remove the last line, irrelevant due to error.
speech.list=cbind(speeches, inaug)
# Add full text of speech to the dataframe
# Loop over each row in speech.list
speech.list$fulltext=NA
for(i in seq(nrow(speech.list))) {
  text <- read_html(speech.list$urls[i]) %>% # load the page
    html_nodes(".displaytext") %>% # isloate the text
    html_text() # get the text
  speech.list$fulltext[i]=text
  # Create the file name
  filename <- paste0("../data/InauguralSpeeches/", 
                     speech.list$type[i],
                     speech.list$File[i], "-", 
                     speech.list$Term[i], ".txt")
  sink(file = filename) %>% # open file to write 
  cat(text)  # write the file
  sink() # close the file
}
# we only need data of democratic and republican
speech.list <- filter(speech.list,Party=='Democratic'|Party=='Republican')
speech.list$Party <- factor(speech.list$Party)
speech.list$File <- paste(speech.list$File,speech.list$Term)
# we also create a sorted speech list according to party
speech.list_sorted <- speech.list[order(speech.list$Party),]
rownames(speech.list_sorted) <- 1:nrow(speech.list_sorted)
speech.list$President <- as.character(speech.list$President)

Step 2: Data Processing — generate list of sentences and list of words

We will generate a list of sentences and a list of words for futher analysis, since sentence and word are unit for our analysis.

Generate list of sentences

We first generate a list of senteences, each row is a sentence of the speech for each president.

# We assign an sequential id to each sentence in a speech (`sent.id`) and also calculated the number of words in each sentence as sentence length (`word.count`).
sentence.list=NULL
for(i in 1:nrow(speech.list)){
  sentences=sent_detect(speech.list$fulltext[i],
                        endmarks = c("?", ".", "!", "|",";"))
  if(length(sentences)>0){
    emotions=get_nrc_sentiment(sentences)
    word.count=word_count(sentences)
    # colnames(emotions)=paste0("emo.", colnames(emotions))
    # in case the word counts are zeros?
    emotions=diag(1/(word.count+0.01))%*%as.matrix(emotions)
    sentence.list=rbind(sentence.list, 
                        cbind(speech.list[i,-ncol(speech.list)],
                              sentences=as.character(sentences), 
                              word.count,
                              emotions,
                              sent.id=1:length(sentences)
                              )
    )
  }
}
# Some non-sentences exist in raw data due to erroneous extra end-of sentence marks. 
sentence.list=
  sentence.list%>%
  filter(!is.na(word.count)) 

Generate list of words

Now we have list of sentences with each row a sentence in the ‘sentences’ column. Now we want to convert it to one-token-per-document-per-row. So we need to break sentences into individual tokens, which could be done by unnest_tokens from tidytext. Below we then generate a list of words from sentence list above, each row is a word of a sentence for each president.

sentence.list$sentences <- as.character(sentence.list$sentences)
words.list <- sentence.list %>%
  unnest_tokens(word, sentences)
#data(stop_words)
words.list <- words.list %>%
  anti_join(stop_words)
Joining, by = "word"
#words.list
words.list$File <- paste(words.list$File,words.list$Term,sep='')
words.list$links <- as.character(as.Date(words.list$links, format="%B %d, %Y"))

Step 3: Total words and length of sentences

Total words of speech

The first question we want to address is: Will the presidents of the two parties differ in length of their speechs? To answer to this question, we draw a bar plot showing the speech length for each president chronologically. And we use different colors to differ the two parties.

ggplot(speech.list,aes(x=reorder(File,1:nrow(speech.list)),y=Words,fill=Party))+
  geom_col()+
  theme(axis.text.x=element_text(angle=45, hjust=1))+
  xlab('President')

From this plot, we could see most speeches of republican are longer than those of democratic. We could also check this intuition by calculate mean total words for two parties.

tapply(speech.list$Words,speech.list$Party,mean)
Democratic Republican 
  2012.682   2533.083 

So mean total words of republican are 500 more than cemocratic.

We could also see the detailed distribution for each parties by violin plot.

ggplot(speech.list, aes(factor(Party), Words,fill=Party)) + 
  geom_violin()+
  xlab('Party')

From the violin plot, we could see speech length of democratic is more concentrated, most around 1600 words. While speech length of republican is more dispersive, most ranging from 1000 to 3000 words.

Length of sentences

We also wonder if length of sentence differ between two parties. So we here draw a violin plot for each president, showing in the same plot. Again, we use different colors to differ two parties.

ggplot(sentence.list,aes(reorder(President,1:nrow(sentence.list)),word.count,fill=Party))+
  geom_violin()+
  xlab('President')+
  coord_flip()

We could also show the mean sentence length for each party.

tapply(sentence.list$word.count,sentence.list$Party,mean)
Democratic Republican 
  23.11024   21.78651 

We could see mean sentence length of republican is smaller than that of democratic.

Summarize from length of speech and length of sentence, president of democratic tend to use shorter speech but with longer sentences.

Step 4: Most frequent words

In this part, we will analyze most frequent words in the speeches for two parties. And we use word cloud to visualize the result.

Read in the speeches

Corpus is a collection of documents, there are many ways to create a corpus, here we use ‘fulltext’ column of the ‘speech.list’ dataframe.

corpus_demo <- Corpus(VectorSource(speech.list_sorted$fulltext[1:22]))
corpus_repub <- Corpus(VectorSource(speech.list_sorted$fulltext[23:46]))

Text processing

For the speeches, we remove extra white space, convert all letters to the lower case, remove stop words, removed empty words due to formatting errors, and remove punctuation.

After cleaning up corpus, we compute TDM. Documemnt Term Matrix (DTM) or Term Document Matrix (TDM) is a document that lists all occurrences of words of each document.

# Corpus and TDM for democratic 
corpus_demo<-tm_map(corpus_demo, stripWhitespace)
corpus_demo<-tm_map(corpus_demo, content_transformer(tolower))
corpus_demo<-tm_map(corpus_demo, removeWords, stopwords("english"))
corpus_demo<-tm_map(corpus_demo, removeWords, character(0))
corpus_demo<-tm_map(corpus_demo, removePunctuation)
tdm.all_demo<-TermDocumentMatrix(corpus_demo)
tdm.tidy_demo=tidy(tdm.all_demo)
tdm.overall_demo=summarise(group_by(tdm.tidy_demo, term), sum(count))
# Corpus and TDM for republican
corpus_repub<-tm_map(corpus_repub, stripWhitespace)
corpus_repub<-tm_map(corpus_repub, content_transformer(tolower))
corpus_repub<-tm_map(corpus_repub, removeWords, stopwords("english"))
corpus_repub<-tm_map(corpus_repub, removeWords, character(0))
corpus_repub<-tm_map(corpus_repub, removePunctuation)
tdm.all_repub<-TermDocumentMatrix(corpus_repub)
tdm.tidy_repub=tidy(tdm.all_repub)
tdm.overall_repub=summarise(group_by(tdm.tidy_repub, term), sum(count))

Word cloud

Next, we create word cloud to visualize the most frequent words for two parties.

# set seed so that we get the same result every run
set.seed(42)
par(mfrow=c(1,2))
# word cloud for democratic
wordcloud(tdm.overall_demo$term, tdm.overall_demo$`sum(count)`,
          scale=c(5,0.5),
          max.words=100,
          min.freq=1,
          random.order=FALSE,
          rot.per=0.3,
          use.r.layout=T,
          random.color=FALSE,
          colors=brewer.pal(6,'Dark2'))
# word cloud for republican
wordcloud(tdm.overall_repub$term, tdm.overall_repub$`sum(count)`,
          scale=c(5,0.5),
          max.words=100,
          min.freq=1,
          random.order=FALSE,
          rot.per=0.3,
          use.r.layout=T,
          random.color=FALSE,
          colors=brewer.pal(6,"Dark2"))

The left plot is word cloud for democratic, the right one is for republican.

It turns out that the most frequent words in the speeches for both parties are similar. For example, ‘will’, ‘government’, ‘people’ show most time, which is intuitively.

We could also show a barplot to show most frequent words.

# bar plot for democratic
a <- ggplot(subset(tdm.overall_demo,`sum(count)`>100),aes(reorder(term,desc(`sum(count)`)),`sum(count)`))+
  geom_bar(stat='identity',fill='tomato')+
  theme(axis.text.x=element_text(angle=45, hjust=1))+
  labs(title='Democratic',x='Term',y='Count')
# bar plot for republican
b <- ggplot(subset(tdm.overall_repub,`sum(count)`>100),aes(reorder(term,desc(`sum(count)`)),`sum(count)`))+
  geom_bar(stat='identity',fill='skyblue')+
  theme(axis.text.x=element_text(angle=45, hjust=1))+
  labs(title='Republican',x='Term',y='Count')
 
# make two plots side by side
grid.arrange(a, b, nrow=1, ncol=2)

We also could compare appearance of some typical words for the two parties like ‘freedom’, ‘nation’, ‘world’, ‘people’, ‘govenment’.

word_trend <- function(word){
  temp <- ddply(words.list,.(File),function(x){
  return(c(unique(x[,'links']),sum(x[,'word']==word),unique(as.character(x[,'Party']))))}) %>% 
  arrange(V1)
  colnames(temp) <- c('File','Date','Count','Party')
  temp$Count <- as.integer(temp$Count)
  
  ggplot(temp,aes(File,Count,group=Party,color=Party))+
    geom_point()+geom_line()+
    theme(axis.text.x=element_text(angle=45, hjust=1))+
    xlab('President')+
    ggtitle(word)
  
}
word_trend('freedom')

word_trend('nation')

word_trend('world')

word_trend('people')

word_trend('government')

word_trend('america')

We see from the plot that for most words, there is no much difference between two parties, but we could also find some interesting patterns like: Republican mentioned ‘freedom’ more than Democratic; Donald Trump mentioned ‘America’ more than any other president.

tf-idf

The idea of tf-idf is to find the important words for the content of each document by decreasing the weight for commonly used words and increasing the weight for words that are not used very much in a collection or corpus of documents.

We show below the top tf-idf score words in the speech.

# tf-idf for democratic
dtm_demo <- DocumentTermMatrix(corpus_demo,
                          control = list(weighting = function(x)
                                             weightTfIdf(x, 
                                                         normalize =FALSE),
                                         stopwords = TRUE))
tf_idf_demo <- tidy(dtm_demo)
print(head(arrange(tf_idf_demo,desc(count)),10))
# tf-idf for republican
dtm_repub <- DocumentTermMatrix(corpus_repub,
                          control = list(weighting = function(x)
                                             weightTfIdf(x, 
                                                         normalize =FALSE),
                                         stopwords = TRUE))
tf_idf_repub <- tidy(dtm_repub)
print(head(arrange(tf_idf_repub,desc(count)),10))

Step 5: Sentiment Analysis

Emotion

There are a variety of methods and dictionaries that exist for evaluating the opinion or emotion in text. The tidytext package contains several sentiment lexicons in the sentiments dataset. There are nrc, bing, and AFINN lexicon, here we use NRC sentiment lexion., which categorizes words in a binary fashion (“yes”/“no”) into categories of positive, negative, anger, anticipation, disgust, fear, joy, sadness, surprise, and trust. More information could be found here.

# calculate means for each emotion
emo.means_demo=colMeans(select(filter(sentence.list,Party=='Democratic'), anger:trust)>0.01)
emo.means_repub=colMeans(select(filter(sentence.list,Party=='Republican'), anger:trust)>0.01)
# use barplot to show emotion for the two parties
color_use <- c('darkgoldenrod1','chocolate1','coral1','dodgerblue3','red','cadetblue3','gold','green3')
c <- ggplot(data.frame(emotion=names(emo.means_demo),number=emo.means_demo),aes(reorder(emotion,number),number))+
  geom_bar(stat='identity',fill=rev(color_use))+
  xlab('Emotion')+
  ggtitle('Democratic')+
  scale_y_reverse()+
  coord_flip()+
  theme_grey()
d <- ggplot(data.frame(emotion=names(emo.means_repub),number=emo.means_repub),aes(reorder(emotion,number),number))+
  geom_bar(stat='identity',fill=rev(color_use))+
  xlab('')+
  ggtitle('Republican')+
  coord_flip()+
  theme_grey()
  
  
grid.arrange(c, d, nrow=1, ncol=2)

We could see similarities of the two parties. Top emotions are both ‘trust’, ‘anticipation’, and ‘joy’, which are all positive emotions. This is intuitive because inauguration speech is meant to inspire people.

Positive & Negative

We could also see the positive and negative sentiment change throughout speechs of each president.

# here we select four presidents to discuss
select_president <- c('John F. Kennedy', 'Barack Obama','George Bush', 'Donald J. Trump')
sentiment <- filter(words.list,President%in%select_president) %>%
  inner_join(get_sentiments("bing")) %>%
  count(File, index = sent.id, sentiment) %>%
  spread(sentiment, n, fill = 0) %>%
  mutate(sentiment = positive - negative)
ggplot(sentiment, aes(index, sentiment, fill = File)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~File, ncol = 2, scales = "free_x")+
  theme_gray()

Barack Obama and John F. Kennedy represent Democratic, while Donald J. Trump and George Bush represent Republican. We could see that Democratic seem to have a negative part after the beginning of the speech, while Republican seem to have positive and negative part throughout the speech.

LS0tCnRpdGxlOiAiRGVtb2NyYXRpYyBWUyBSZXB1YmxpY2FuIC0tIFdoYXQgY2FuIHdlIGtub3cgZnJvbSBwcmVzaWRlbnQgaW5hdWd1cmF0aW9uIHNwZWVjaGVzPyIKYXV0aG9yOiAiSGFuIExpbiAoaGwzMDA2KSIKZGF0ZTogIjkvMTcvMjAxNyIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgo8ZGl2IHN0eWxlPSJ3aWR0aDo0MDBweDsgaGVpZ2h0PTMwMHB4Ij4KIVtdKGh0dHA6Ly9hdGxhbnRhYmxhY2tzdGFyLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxMy8wMy9yZXB1YmxpY2FuLWRlbW9jcmF0LWJhdHRsZS5qcGVnKQo8L2Rpdj4gCgoKQXMgd2UgYWxsIGtub3csIERlbW9jcmF0aWMgUGFydHkgYW5kIFJlcHVibGljYW4gUGFydHkgYXJlIHR3byBtYWpvciBjb250ZW1wb3JhcnkgcG9saXRpY2FsIHBhcnRpZXMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMsIHRoZSBmb3JtZXIgaXMgbW9yZSBwcm9ncmVzc2l2ZSBhbmQgdGhlIGxhdHRlciBpcyBtb3JlIGNvbnNlcnZhdGl2ZS4KVGhlIHR3byBwYXJ0aWVzIGRpZmZlciBpbiB0aGVpciBwb2xpY2llcyBidXQgYWxzbyBoYXZlIHNpbWlsYXJpdGllcy4KCkFzIEFtZXJpY2FuIHBlb3BsZSB2b3RlIHRvIGRlY2lkZSBuZXh0IHByZXNpZGVudCwgdGhleSBhcmUgYmFzaWNhbGx5IGNob29zaW5nIHBhcnRpZXMsIGluIG1vc3QgY2FzZXMsIGJldHdlZW4gRGVtb2NyYXRpYyBhbmQgUmVwdWJsaWNhbi4gU28gb25lIHF1ZXN0aW9uIHdlIGNvdWxkIGFzayBpczogd2hhdCBhcmUgdGhlIGRpZmZlcmVuY2VzIGFuZCBzaW1pbGFyaXRpZXMgb2YgdGhlIHByZXNpZGVudCBpbmF1Z3VyYXRpb24gc3BlZWNoZXMgZnJvbSBkaWZmZXJlbnQgcGFydGllcz8KCkluIHRoaXMgcHJvamVjdCwgSSB3aWxsIGFuYWx5emUgaW5hdWd1cmF0aW9uIHNwZWVjaGVzIG9mIGRpZmZlcmVudCBwcmVzaWRlbnRzIGZyb20gRGVtb2NyYXRpYyBhbmQgUmVwdWJsaWNhbiwgdHJ5aW5nIHRvIGZpbmQgZGlmZmVyZW5jZXMgYW5kIHNpbWlsYXJpdGllcy4gSGVyZSBpcyBzb21lIHF1ZXN0aW9ucyB3ZSB3YW50IHRvIGZpbmQgdGhlIGFuc3dlcjoKCgoKKiBXaWxsIHRoZSBwcmVzaWRlbnRzIG9mIHRoZSB0d28gcGFydGllcyBkaWZmZXIgaW4gbGVuZ3RoIG9mIHRoZWlyIHNwZWVjaHM/CgoqIEFyZSB0aGVyZSBhbnkgZGlmZmVyZW5jZXMgb2YgdGhlIGxlbmd0aCBvZiBzZW50ZW5jZSBvZiBzcGVlY2hzIGZvciB0d28gcGFydGllcz8KCiogV2hhdCBhcmUgdGhlIG1vc3QgZnJlcXVlbnQgc2FpZCB3b3JkcyBpbiB0aGUgc3BlZWNoIGZvciB0d28gcGFydGllcz8KCiogSG93IGRvIHR5cGljYWwgd29yZHMgbGlrZSAnZnJlZWRvbScsICdwZW9wbGUnLCAnZ292ZXJubWVudCcgc2hvdyBpbiB0aGUgc3BlZWNoZXM/CgoqIEhvdyBkb2VzIGVtb3Rpb24gY2hhbmdlIGluIHRoZSBzcGVlY2hlcyBhbmQgY29tcGFyZWQgYmV0d2VlbiB0d28gcGFydGllcz8KCgoKIyBTdGVwIDA6IEluc3RhbGwgYW5kIGxvYWQgbGlicmFyaWVzCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLHJlc3VsdHM9J2hpZGUnfQpwYWNrYWdlcy51c2VkPWMoInJ2ZXN0IiwgInFkYXAiLCAgInRpZHl0ZXh0IiwicGx5ciIsInN5dXpoZXQiLAogICAgICAgICAgICAgICAgInNlbnRpbWVudHIiLCAiZ3Bsb3RzIiwgImRwbHlyIiwidGlkeXIiLCJncmlkRXh0cmEiLAogICAgICAgICAgICAgICAgInRtIiwidG9waWNtb2RlbHMiLCJnZ3Bsb3QyIiwid29yZGNsb3VkIiwiUkNvbG9yQnJld2VyIikKIyBjaGVjayBwYWNrYWdlcyB0aGF0IG5lZWQgdG8gYmUgaW5zdGFsbGVkLgpwYWNrYWdlcy5uZWVkZWQ9c2V0ZGlmZihwYWNrYWdlcy51c2VkLCAKICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJzZWN0KGluc3RhbGxlZC5wYWNrYWdlcygpWywxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYWNrYWdlcy51c2VkKSkKIyBpbnN0YWxsIGFkZGl0aW9uYWwgcGFja2FnZXMKaWYobGVuZ3RoKHBhY2thZ2VzLm5lZWRlZCk+MCl7CiAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlcy5uZWVkZWQsIGRlcGVuZGVuY2llcyA9IFRSVUUpCn0KCmxhcHBseShwYWNrYWdlcy51c2VkLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCgpzb3VyY2UoIi4uL2xpYi9wbG90c3RhY2tlZC5SIikKc291cmNlKCIuLi9saWIvc3BlZWNoRnVuY3MuUiIpCgoKYGBgCgpUaGlzIG5vdGVib29rIHdhcyBwcmVwYXJlZCB3aXRoIHRoZSBmb2xsb3dpbmcgZW52aXJvbm1lbnRhbCBzZXR0aW5ncy4KCmBgYHtyfQpwcmludChSLnZlcnNpb24pCmBgYAoKCiMgU3RlcCAxOiBJbXBvcnQgZGF0YQoKSW4gdGhpcyBwcm9qZWN0LCB3ZSBuZWVkIGluZm9ybWF0aW9uIGFib3V0IHByZXNpZGVudCBpbmF1Z3VyYXRpb24gc3BlZWNoZXMuIFdlIGhhdmUgYmFzaWMgaW5mb3JtYXRpb24gaW4gJ0luYXVndXJhdGlvbkluZm8uY3N2JyBhbmQgd2Ugc2NyYXBlZCBzcGVlY2ggVVJMcyBmcm9tIDxodHRwOi8vd3d3LnByZXNpZGVuY3kudWNzYi5lZHUvaW5hdWd1cmFscy5waHA+LgoKYGBge3J9CiMgYmFzaWMgaW5mb3JtYXRpb24gYWJvdXQgaW5hdWd1cmF0aW9uIHNwZWVjaApzcGVlY2hlcyA8LSByZWFkLmNzdigiLi4vZGF0YS9JbmF1Z3VyYXRpb25JbmZvLmNzdiIpCgojIGluYXVndWFyYWwgc3BlZWNoZXMKbWFpbi5wYWdlIDwtIHJlYWRfaHRtbCh4ID0gImh0dHA6Ly93d3cucHJlc2lkZW5jeS51Y3NiLmVkdS9pbmF1Z3VyYWxzLnBocCIpCiMgR2V0IGxpbmsgVVJMcwojIGYuc3BlZWNobGlua3MgaXMgYSBmdW5jdGlvbiBmb3IgZXh0cmFjdGluZyBsaW5rcyBmcm9tIHRoZSBsaXN0IG9mIHNwZWVjaGVzLiAKaW5hdWc9Zi5zcGVlY2hsaW5rcyhtYWluLnBhZ2UpCiNoZWFkKGluYXVnKQojYXMuRGF0ZShpbmF1Z1ssMV0sIGZvcm1hdD0iJUIgJWUsICVZIikKaW5hdWc9aW5hdWdbLW5yb3coaW5hdWcpLF0gIyByZW1vdmUgdGhlIGxhc3QgbGluZSwgaXJyZWxldmFudCBkdWUgdG8gZXJyb3IuCnNwZWVjaC5saXN0PWNiaW5kKHNwZWVjaGVzLCBpbmF1ZykKCiMgQWRkIGZ1bGwgdGV4dCBvZiBzcGVlY2ggdG8gdGhlIGRhdGFmcmFtZQojIExvb3Agb3ZlciBlYWNoIHJvdyBpbiBzcGVlY2gubGlzdApzcGVlY2gubGlzdCRmdWxsdGV4dD1OQQpmb3IoaSBpbiBzZXEobnJvdyhzcGVlY2gubGlzdCkpKSB7CiAgdGV4dCA8LSByZWFkX2h0bWwoc3BlZWNoLmxpc3QkdXJsc1tpXSkgJT4lICMgbG9hZCB0aGUgcGFnZQogICAgaHRtbF9ub2RlcygiLmRpc3BsYXl0ZXh0IikgJT4lICMgaXNsb2F0ZSB0aGUgdGV4dAogICAgaHRtbF90ZXh0KCkgIyBnZXQgdGhlIHRleHQKICBzcGVlY2gubGlzdCRmdWxsdGV4dFtpXT10ZXh0CiAgIyBDcmVhdGUgdGhlIGZpbGUgbmFtZQogIGZpbGVuYW1lIDwtIHBhc3RlMCgiLi4vZGF0YS9JbmF1Z3VyYWxTcGVlY2hlcy8iLCAKICAgICAgICAgICAgICAgICAgICAgc3BlZWNoLmxpc3QkdHlwZVtpXSwKICAgICAgICAgICAgICAgICAgICAgc3BlZWNoLmxpc3QkRmlsZVtpXSwgIi0iLCAKICAgICAgICAgICAgICAgICAgICAgc3BlZWNoLmxpc3QkVGVybVtpXSwgIi50eHQiKQogIHNpbmsoZmlsZSA9IGZpbGVuYW1lKSAlPiUgIyBvcGVuIGZpbGUgdG8gd3JpdGUgCiAgY2F0KHRleHQpICAjIHdyaXRlIHRoZSBmaWxlCiAgc2luaygpICMgY2xvc2UgdGhlIGZpbGUKfQoKIyB3ZSBvbmx5IG5lZWQgZGF0YSBvZiBkZW1vY3JhdGljIGFuZCByZXB1YmxpY2FuCnNwZWVjaC5saXN0IDwtIGZpbHRlcihzcGVlY2gubGlzdCxQYXJ0eT09J0RlbW9jcmF0aWMnfFBhcnR5PT0nUmVwdWJsaWNhbicpCnNwZWVjaC5saXN0JFBhcnR5IDwtIGZhY3RvcihzcGVlY2gubGlzdCRQYXJ0eSkKc3BlZWNoLmxpc3QkRmlsZSA8LSBwYXN0ZShzcGVlY2gubGlzdCRGaWxlLHNwZWVjaC5saXN0JFRlcm0pCgojIHdlIGFsc28gY3JlYXRlIGEgc29ydGVkIHNwZWVjaCBsaXN0IGFjY29yZGluZyB0byBwYXJ0eQpzcGVlY2gubGlzdF9zb3J0ZWQgPC0gc3BlZWNoLmxpc3Rbb3JkZXIoc3BlZWNoLmxpc3QkUGFydHkpLF0Kcm93bmFtZXMoc3BlZWNoLmxpc3Rfc29ydGVkKSA8LSAxOm5yb3coc3BlZWNoLmxpc3Rfc29ydGVkKQpzcGVlY2gubGlzdCRQcmVzaWRlbnQgPC0gYXMuY2hhcmFjdGVyKHNwZWVjaC5saXN0JFByZXNpZGVudCkKCgpgYGAKCiMgU3RlcCAyOiBEYXRhIFByb2Nlc3NpbmcgLS0tIGdlbmVyYXRlIGxpc3Qgb2Ygc2VudGVuY2VzIGFuZCBsaXN0IG9mIHdvcmRzCgpXZSB3aWxsIGdlbmVyYXRlIGEgbGlzdCBvZiBzZW50ZW5jZXMgYW5kIGEgbGlzdCBvZiB3b3JkcyBmb3IgZnV0aGVyIGFuYWx5c2lzLCBzaW5jZSBzZW50ZW5jZSBhbmQgd29yZCBhcmUgdW5pdCBmb3Igb3VyIGFuYWx5c2lzLgoKIyMgR2VuZXJhdGUgbGlzdCBvZiBzZW50ZW5jZXMKCldlIGZpcnN0IGdlbmVyYXRlIGEgbGlzdCBvZiBzZW50ZWVuY2VzLCBlYWNoIHJvdyBpcyBhIHNlbnRlbmNlIG9mIHRoZSBzcGVlY2ggZm9yIGVhY2ggcHJlc2lkZW50LgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBXZSBhc3NpZ24gYW4gc2VxdWVudGlhbCBpZCB0byBlYWNoIHNlbnRlbmNlIGluIGEgc3BlZWNoIChgc2VudC5pZGApIGFuZCBhbHNvIGNhbGN1bGF0ZWQgdGhlIG51bWJlciBvZiB3b3JkcyBpbiBlYWNoIHNlbnRlbmNlIGFzIHNlbnRlbmNlIGxlbmd0aCAoYHdvcmQuY291bnRgKS4Kc2VudGVuY2UubGlzdD1OVUxMCmZvcihpIGluIDE6bnJvdyhzcGVlY2gubGlzdCkpewogIHNlbnRlbmNlcz1zZW50X2RldGVjdChzcGVlY2gubGlzdCRmdWxsdGV4dFtpXSwKICAgICAgICAgICAgICAgICAgICAgICAgZW5kbWFya3MgPSBjKCI/IiwgIi4iLCAiISIsICJ8IiwiOyIpKQogIGlmKGxlbmd0aChzZW50ZW5jZXMpPjApewogICAgZW1vdGlvbnM9Z2V0X25yY19zZW50aW1lbnQoc2VudGVuY2VzKQogICAgd29yZC5jb3VudD13b3JkX2NvdW50KHNlbnRlbmNlcykKICAgICMgY29sbmFtZXMoZW1vdGlvbnMpPXBhc3RlMCgiZW1vLiIsIGNvbG5hbWVzKGVtb3Rpb25zKSkKICAgICMgaW4gY2FzZSB0aGUgd29yZCBjb3VudHMgYXJlIHplcm9zPwogICAgZW1vdGlvbnM9ZGlhZygxLyh3b3JkLmNvdW50KzAuMDEpKSUqJWFzLm1hdHJpeChlbW90aW9ucykKICAgIHNlbnRlbmNlLmxpc3Q9cmJpbmQoc2VudGVuY2UubGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKHNwZWVjaC5saXN0W2ksLW5jb2woc3BlZWNoLmxpc3QpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VudGVuY2VzPWFzLmNoYXJhY3RlcihzZW50ZW5jZXMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd29yZC5jb3VudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW1vdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnQuaWQ9MTpsZW5ndGgoc2VudGVuY2VzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICApCiAgfQp9CgojIFNvbWUgbm9uLXNlbnRlbmNlcyBleGlzdCBpbiByYXcgZGF0YSBkdWUgdG8gZXJyb25lb3VzIGV4dHJhIGVuZC1vZiBzZW50ZW5jZSBtYXJrcy4gCnNlbnRlbmNlLmxpc3Q9CiAgc2VudGVuY2UubGlzdCU+JQogIGZpbHRlcighaXMubmEod29yZC5jb3VudCkpIAoKYGBgCgoKIyMgR2VuZXJhdGUgbGlzdCBvZiB3b3JkcwoKTm93IHdlIGhhdmUgbGlzdCBvZiBzZW50ZW5jZXMgd2l0aCBlYWNoIHJvdyBhIHNlbnRlbmNlIGluIHRoZSAnc2VudGVuY2VzJyBjb2x1bW4uIE5vdyB3ZSB3YW50IHRvIGNvbnZlcnQgaXQgdG8gb25lLXRva2VuLXBlci1kb2N1bWVudC1wZXItcm93LiBTbyB3ZSBuZWVkIHRvIGJyZWFrIHNlbnRlbmNlcyBpbnRvIGluZGl2aWR1YWwgdG9rZW5zLCB3aGljaCBjb3VsZCBiZSBkb25lIGJ5ICp1bm5lc3RfdG9rZW5zKiBmcm9tIHRpZHl0ZXh0LiBCZWxvdyB3ZSB0aGVuIGdlbmVyYXRlIGEgbGlzdCBvZiB3b3JkcyBmcm9tIHNlbnRlbmNlIGxpc3QgYWJvdmUsIGVhY2ggcm93IGlzIGEgd29yZCBvZiBhIHNlbnRlbmNlIGZvciBlYWNoIHByZXNpZGVudC4gCmBgYHtyfQpzZW50ZW5jZS5saXN0JHNlbnRlbmNlcyA8LSBhcy5jaGFyYWN0ZXIoc2VudGVuY2UubGlzdCRzZW50ZW5jZXMpCndvcmRzLmxpc3QgPC0gc2VudGVuY2UubGlzdCAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnRlbmNlcykKI2RhdGEoc3RvcF93b3JkcykKd29yZHMubGlzdCA8LSB3b3Jkcy5saXN0ICU+JQogIGFudGlfam9pbihzdG9wX3dvcmRzKQojd29yZHMubGlzdAp3b3Jkcy5saXN0JEZpbGUgPC0gcGFzdGUod29yZHMubGlzdCRGaWxlLHdvcmRzLmxpc3QkVGVybSxzZXA9JycpCndvcmRzLmxpc3QkbGlua3MgPC0gYXMuY2hhcmFjdGVyKGFzLkRhdGUod29yZHMubGlzdCRsaW5rcywgZm9ybWF0PSIlQiAlZCwgJVkiKSkKYGBgCgoKCiMgU3RlcCAzOiBUb3RhbCB3b3JkcyBhbmQgbGVuZ3RoIG9mIHNlbnRlbmNlcwoKIyMgVG90YWwgd29yZHMgb2Ygc3BlZWNoClRoZSBmaXJzdCBxdWVzdGlvbiB3ZSB3YW50IHRvIGFkZHJlc3MgaXM6IFdpbGwgdGhlIHByZXNpZGVudHMgb2YgdGhlIHR3byBwYXJ0aWVzIGRpZmZlciBpbiBsZW5ndGggb2YgdGhlaXIgc3BlZWNocz8gVG8gYW5zd2VyIHRvIHRoaXMgcXVlc3Rpb24sIHdlIGRyYXcgYSBiYXIgcGxvdCBzaG93aW5nIHRoZSBzcGVlY2ggbGVuZ3RoIGZvciBlYWNoIHByZXNpZGVudCBjaHJvbm9sb2dpY2FsbHkuIEFuZCB3ZSB1c2UgZGlmZmVyZW50IGNvbG9ycyB0byBkaWZmZXIgdGhlIHR3byBwYXJ0aWVzLgoKYGBge3J9CmdncGxvdChzcGVlY2gubGlzdCxhZXMoeD1yZW9yZGVyKEZpbGUsMTpucm93KHNwZWVjaC5saXN0KSkseT1Xb3JkcyxmaWxsPVBhcnR5KSkrCiAgZ2VvbV9jb2woKSsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSsKICB4bGFiKCdQcmVzaWRlbnQnKQpgYGAKCkZyb20gdGhpcyBwbG90LCB3ZSBjb3VsZCBzZWUgbW9zdCBzcGVlY2hlcyBvZiByZXB1YmxpY2FuIGFyZSBsb25nZXIgdGhhbiB0aG9zZSBvZiBkZW1vY3JhdGljLiBXZSBjb3VsZCBhbHNvIGNoZWNrIHRoaXMgaW50dWl0aW9uIGJ5IGNhbGN1bGF0ZSBtZWFuIHRvdGFsIHdvcmRzIGZvciB0d28gcGFydGllcy4KCmBgYHtyfQp0YXBwbHkoc3BlZWNoLmxpc3QkV29yZHMsc3BlZWNoLmxpc3QkUGFydHksbWVhbikKYGBgClNvIG1lYW4gdG90YWwgd29yZHMgb2YgcmVwdWJsaWNhbiBhcmUgNTAwIG1vcmUgdGhhbiBjZW1vY3JhdGljLiAKCldlIGNvdWxkIGFsc28gc2VlIHRoZSBkZXRhaWxlZCBkaXN0cmlidXRpb24gZm9yIGVhY2ggcGFydGllcyBieSB2aW9saW4gcGxvdC4KYGBge3J9CmdncGxvdChzcGVlY2gubGlzdCwgYWVzKGZhY3RvcihQYXJ0eSksIFdvcmRzLGZpbGw9UGFydHkpKSArIAogIGdlb21fdmlvbGluKCkrCiAgeGxhYignUGFydHknKQpgYGAKCkZyb20gdGhlIHZpb2xpbiBwbG90LCB3ZSBjb3VsZCBzZWUgc3BlZWNoIGxlbmd0aCBvZiBkZW1vY3JhdGljIGlzIG1vcmUgY29uY2VudHJhdGVkLCBtb3N0IGFyb3VuZCAxNjAwIHdvcmRzLiBXaGlsZSBzcGVlY2ggbGVuZ3RoIG9mIHJlcHVibGljYW4gaXMgbW9yZSBkaXNwZXJzaXZlLCBtb3N0IHJhbmdpbmcgZnJvbSAxMDAwIHRvIDMwMDAgd29yZHMuCgoKIyMgTGVuZ3RoIG9mIHNlbnRlbmNlcwpXZSBhbHNvIHdvbmRlciBpZiBsZW5ndGggb2Ygc2VudGVuY2UgZGlmZmVyIGJldHdlZW4gdHdvIHBhcnRpZXMuIFNvIHdlIGhlcmUgZHJhdyBhIHZpb2xpbiBwbG90IGZvciBlYWNoIHByZXNpZGVudCwgc2hvd2luZyBpbiB0aGUgc2FtZSBwbG90LiBBZ2Fpbiwgd2UgdXNlIGRpZmZlcmVudCBjb2xvcnMgdG8gZGlmZmVyIHR3byBwYXJ0aWVzLgoKYGBge3J9CmdncGxvdChzZW50ZW5jZS5saXN0LGFlcyhyZW9yZGVyKFByZXNpZGVudCwxOm5yb3coc2VudGVuY2UubGlzdCkpLHdvcmQuY291bnQsZmlsbD1QYXJ0eSkpKwogIGdlb21fdmlvbGluKCkrCiAgeGxhYignUHJlc2lkZW50JykrCiAgY29vcmRfZmxpcCgpCgpgYGAKCldlIGNvdWxkIGFsc28gc2hvdyB0aGUgbWVhbiBzZW50ZW5jZSBsZW5ndGggZm9yIGVhY2ggcGFydHkuCmBgYHtyfQp0YXBwbHkoc2VudGVuY2UubGlzdCR3b3JkLmNvdW50LHNlbnRlbmNlLmxpc3QkUGFydHksbWVhbikKYGBgCldlIGNvdWxkIHNlZSBtZWFuIHNlbnRlbmNlIGxlbmd0aCBvZiByZXB1YmxpY2FuIGlzIHNtYWxsZXIgdGhhbiB0aGF0IG9mIGRlbW9jcmF0aWMuCgpTdW1tYXJpemUgZnJvbSBsZW5ndGggb2Ygc3BlZWNoIGFuZCBsZW5ndGggb2Ygc2VudGVuY2UsIHByZXNpZGVudCBvZiBkZW1vY3JhdGljIHRlbmQgdG8gdXNlIHNob3J0ZXIgc3BlZWNoIGJ1dCB3aXRoIGxvbmdlciBzZW50ZW5jZXMuCgoKIyBTdGVwIDQ6IE1vc3QgZnJlcXVlbnQgd29yZHMKCkluIHRoaXMgcGFydCwgd2Ugd2lsbCBhbmFseXplIG1vc3QgZnJlcXVlbnQgd29yZHMgaW4gdGhlIHNwZWVjaGVzIGZvciB0d28gcGFydGllcy4gQW5kIHdlIHVzZSB3b3JkIGNsb3VkIHRvIHZpc3VhbGl6ZSB0aGUgcmVzdWx0LgoKIyMgUmVhZCBpbiB0aGUgc3BlZWNoZXMKCkNvcnB1cyBpcyBhIGNvbGxlY3Rpb24gb2YgZG9jdW1lbnRzLCB0aGVyZSBhcmUgbWFueSB3YXlzIHRvIGNyZWF0ZSBhIGNvcnB1cywgaGVyZSB3ZSB1c2UgJ2Z1bGx0ZXh0JyBjb2x1bW4gb2YgdGhlICdzcGVlY2gubGlzdCcgZGF0YWZyYW1lLgoKYGBge3J9CmNvcnB1c19kZW1vIDwtIENvcnB1cyhWZWN0b3JTb3VyY2Uoc3BlZWNoLmxpc3Rfc29ydGVkJGZ1bGx0ZXh0WzE6MjJdKSkKY29ycHVzX3JlcHViIDwtIENvcnB1cyhWZWN0b3JTb3VyY2Uoc3BlZWNoLmxpc3Rfc29ydGVkJGZ1bGx0ZXh0WzIzOjQ2XSkpCmBgYAoKIyMgVGV4dCBwcm9jZXNzaW5nCgpGb3IgdGhlIHNwZWVjaGVzLCB3ZSByZW1vdmUgZXh0cmEgd2hpdGUgc3BhY2UsIGNvbnZlcnQgYWxsIGxldHRlcnMgdG8gdGhlIGxvd2VyIGNhc2UsIHJlbW92ZSBbc3RvcCB3b3Jkc10oaHR0cHM6Ly9naXRodWIuY29tL2FyYzEyL1RleHQtTWluaW5nLVdlYWstU2lnbmFscy93aWtpL1N0YW5kYXJkLXNldC1vZi1lbmdsaXNoLXN0b3B3b3JkcyksIHJlbW92ZWQgZW1wdHkgd29yZHMgZHVlIHRvIGZvcm1hdHRpbmcgZXJyb3JzLCBhbmQgcmVtb3ZlIHB1bmN0dWF0aW9uLiAKCkFmdGVyIGNsZWFuaW5nIHVwIGNvcnB1cywgd2UgY29tcHV0ZSBURE0uIERvY3VtZW1udCBUZXJtIE1hdHJpeCAoRFRNKSBvciBUZXJtIERvY3VtZW50IE1hdHJpeCAoVERNKSBpcyBhIGRvY3VtZW50IHRoYXQgbGlzdHMgYWxsIG9jY3VycmVuY2VzIG9mIHdvcmRzIG9mIGVhY2ggZG9jdW1lbnQuCgpgYGB7cn0KIyBDb3JwdXMgYW5kIFRETSBmb3IgZGVtb2NyYXRpYyAKY29ycHVzX2RlbW88LXRtX21hcChjb3JwdXNfZGVtbywgc3RyaXBXaGl0ZXNwYWNlKQpjb3JwdXNfZGVtbzwtdG1fbWFwKGNvcnB1c19kZW1vLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQpjb3JwdXNfZGVtbzwtdG1fbWFwKGNvcnB1c19kZW1vLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpCmNvcnB1c19kZW1vPC10bV9tYXAoY29ycHVzX2RlbW8sIHJlbW92ZVdvcmRzLCBjaGFyYWN0ZXIoMCkpCmNvcnB1c19kZW1vPC10bV9tYXAoY29ycHVzX2RlbW8sIHJlbW92ZVB1bmN0dWF0aW9uKQoKCnRkbS5hbGxfZGVtbzwtVGVybURvY3VtZW50TWF0cml4KGNvcnB1c19kZW1vKQp0ZG0udGlkeV9kZW1vPXRpZHkodGRtLmFsbF9kZW1vKQp0ZG0ub3ZlcmFsbF9kZW1vPXN1bW1hcmlzZShncm91cF9ieSh0ZG0udGlkeV9kZW1vLCB0ZXJtKSwgc3VtKGNvdW50KSkKCgojIENvcnB1cyBhbmQgVERNIGZvciByZXB1YmxpY2FuCmNvcnB1c19yZXB1YjwtdG1fbWFwKGNvcnB1c19yZXB1Yiwgc3RyaXBXaGl0ZXNwYWNlKQpjb3JwdXNfcmVwdWI8LXRtX21hcChjb3JwdXNfcmVwdWIsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpCmNvcnB1c19yZXB1YjwtdG1fbWFwKGNvcnB1c19yZXB1YiwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygiZW5nbGlzaCIpKQpjb3JwdXNfcmVwdWI8LXRtX21hcChjb3JwdXNfcmVwdWIsIHJlbW92ZVdvcmRzLCBjaGFyYWN0ZXIoMCkpCmNvcnB1c19yZXB1YjwtdG1fbWFwKGNvcnB1c19yZXB1YiwgcmVtb3ZlUHVuY3R1YXRpb24pCgoKdGRtLmFsbF9yZXB1YjwtVGVybURvY3VtZW50TWF0cml4KGNvcnB1c19yZXB1YikKdGRtLnRpZHlfcmVwdWI9dGlkeSh0ZG0uYWxsX3JlcHViKQp0ZG0ub3ZlcmFsbF9yZXB1Yj1zdW1tYXJpc2UoZ3JvdXBfYnkodGRtLnRpZHlfcmVwdWIsIHRlcm0pLCBzdW0oY291bnQpKQoKYGBgCgojIyBXb3JkIGNsb3VkCgpOZXh0LCB3ZSBjcmVhdGUgd29yZCBjbG91ZCB0byB2aXN1YWxpemUgdGhlIG1vc3QgZnJlcXVlbnQgd29yZHMgZm9yIHR3byBwYXJ0aWVzLgoKYGBge3Isd2FybmluZz1GQUxTRX0KIyBzZXQgc2VlZCBzbyB0aGF0IHdlIGdldCB0aGUgc2FtZSByZXN1bHQgZXZlcnkgcnVuCnNldC5zZWVkKDQyKQoKcGFyKG1mcm93PWMoMSwyKSkKCiMgd29yZCBjbG91ZCBmb3IgZGVtb2NyYXRpYwp3b3JkY2xvdWQodGRtLm92ZXJhbGxfZGVtbyR0ZXJtLCB0ZG0ub3ZlcmFsbF9kZW1vJGBzdW0oY291bnQpYCwKICAgICAgICAgIHNjYWxlPWMoNSwwLjUpLAogICAgICAgICAgbWF4LndvcmRzPTEwMCwKICAgICAgICAgIG1pbi5mcmVxPTEsCiAgICAgICAgICByYW5kb20ub3JkZXI9RkFMU0UsCiAgICAgICAgICByb3QucGVyPTAuMywKICAgICAgICAgIHVzZS5yLmxheW91dD1ULAogICAgICAgICAgcmFuZG9tLmNvbG9yPUZBTFNFLAogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoNiwnRGFyazInKSkKCiMgd29yZCBjbG91ZCBmb3IgcmVwdWJsaWNhbgp3b3JkY2xvdWQodGRtLm92ZXJhbGxfcmVwdWIkdGVybSwgdGRtLm92ZXJhbGxfcmVwdWIkYHN1bShjb3VudClgLAogICAgICAgICAgc2NhbGU9Yyg1LDAuNSksCiAgICAgICAgICBtYXgud29yZHM9MTAwLAogICAgICAgICAgbWluLmZyZXE9MSwKICAgICAgICAgIHJhbmRvbS5vcmRlcj1GQUxTRSwKICAgICAgICAgIHJvdC5wZXI9MC4zLAogICAgICAgICAgdXNlLnIubGF5b3V0PVQsCiAgICAgICAgICByYW5kb20uY29sb3I9RkFMU0UsCiAgICAgICAgICBjb2xvcnM9YnJld2VyLnBhbCg2LCJEYXJrMiIpKQoKYGBgCgpUaGUgbGVmdCBwbG90IGlzIHdvcmQgY2xvdWQgZm9yIGRlbW9jcmF0aWMsIHRoZSByaWdodCBvbmUgaXMgZm9yIHJlcHVibGljYW4uCgpJdCB0dXJucyBvdXQgdGhhdCB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyBpbiB0aGUgc3BlZWNoZXMgZm9yIGJvdGggcGFydGllcyBhcmUgc2ltaWxhci4gRm9yIGV4YW1wbGUsICd3aWxsJywgJ2dvdmVybm1lbnQnLCAncGVvcGxlJyBzaG93IG1vc3QgdGltZSwgd2hpY2ggaXMgaW50dWl0aXZlbHkuCgpXZSBjb3VsZCBhbHNvIHNob3cgYSBiYXJwbG90IHRvIHNob3cgbW9zdCBmcmVxdWVudCB3b3Jkcy4KCmBgYHtyfQojIGJhciBwbG90IGZvciBkZW1vY3JhdGljCmEgPC0gZ2dwbG90KHN1YnNldCh0ZG0ub3ZlcmFsbF9kZW1vLGBzdW0oY291bnQpYD4xMDApLGFlcyhyZW9yZGVyKHRlcm0sZGVzYyhgc3VtKGNvdW50KWApKSxgc3VtKGNvdW50KWApKSsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsZmlsbD0ndG9tYXRvJykrCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkrCiAgbGFicyh0aXRsZT0nRGVtb2NyYXRpYycseD0nVGVybScseT0nQ291bnQnKQoKIyBiYXIgcGxvdCBmb3IgcmVwdWJsaWNhbgpiIDwtIGdncGxvdChzdWJzZXQodGRtLm92ZXJhbGxfcmVwdWIsYHN1bShjb3VudClgPjEwMCksYWVzKHJlb3JkZXIodGVybSxkZXNjKGBzdW0oY291bnQpYCkpLGBzdW0oY291bnQpYCkpKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JyxmaWxsPSdza3libHVlJykrCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkrCiAgbGFicyh0aXRsZT0nUmVwdWJsaWNhbicseD0nVGVybScseT0nQ291bnQnKQogCiMgbWFrZSB0d28gcGxvdHMgc2lkZSBieSBzaWRlCmdyaWQuYXJyYW5nZShhLCBiLCBucm93PTEsIG5jb2w9MikKCmBgYAoKCldlIGFsc28gY291bGQgY29tcGFyZSBhcHBlYXJhbmNlIG9mIHNvbWUgdHlwaWNhbCB3b3JkcyBmb3IgdGhlIHR3byBwYXJ0aWVzIGxpa2UgJ2ZyZWVkb20nLCAnbmF0aW9uJywgJ3dvcmxkJywgJ3Blb3BsZScsICdnb3Zlbm1lbnQnLgoKYGBge3J9Cgp3b3JkX3RyZW5kIDwtIGZ1bmN0aW9uKHdvcmQpewogIHRlbXAgPC0gZGRwbHkod29yZHMubGlzdCwuKEZpbGUpLGZ1bmN0aW9uKHgpewogIHJldHVybihjKHVuaXF1ZSh4WywnbGlua3MnXSksc3VtKHhbLCd3b3JkJ109PXdvcmQpLHVuaXF1ZShhcy5jaGFyYWN0ZXIoeFssJ1BhcnR5J10pKSkpfSkgJT4lIAogIGFycmFuZ2UoVjEpCiAgY29sbmFtZXModGVtcCkgPC0gYygnRmlsZScsJ0RhdGUnLCdDb3VudCcsJ1BhcnR5JykKICB0ZW1wJENvdW50IDwtIGFzLmludGVnZXIodGVtcCRDb3VudCkKICAKICBnZ3Bsb3QodGVtcCxhZXMoRmlsZSxDb3VudCxncm91cD1QYXJ0eSxjb2xvcj1QYXJ0eSkpKwogICAgZ2VvbV9wb2ludCgpK2dlb21fbGluZSgpKwogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkrCiAgICB4bGFiKCdQcmVzaWRlbnQnKSsKICAgIGdndGl0bGUod29yZCkKICAKfQoKCndvcmRfdHJlbmQoJ2ZyZWVkb20nKQp3b3JkX3RyZW5kKCduYXRpb24nKQp3b3JkX3RyZW5kKCd3b3JsZCcpCndvcmRfdHJlbmQoJ3Blb3BsZScpCndvcmRfdHJlbmQoJ2dvdmVybm1lbnQnKQp3b3JkX3RyZW5kKCdhbWVyaWNhJykKCmBgYAoKV2Ugc2VlIGZyb20gdGhlIHBsb3QgdGhhdCBmb3IgbW9zdCB3b3JkcywgdGhlcmUgaXMgbm8gbXVjaCBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIHBhcnRpZXMsIGJ1dCB3ZSBjb3VsZCBhbHNvIGZpbmQgc29tZSBpbnRlcmVzdGluZyBwYXR0ZXJucyBsaWtlOiBSZXB1YmxpY2FuIG1lbnRpb25lZCAnZnJlZWRvbScgbW9yZSB0aGFuIERlbW9jcmF0aWM7IERvbmFsZCBUcnVtcCBtZW50aW9uZWQgJ0FtZXJpY2EnIG1vcmUgdGhhbiBhbnkgb3RoZXIgcHJlc2lkZW50LgoKCgoKIyMjIHRmLWlkZgoKVGhlIGlkZWEgb2YgdGYtaWRmIGlzIHRvIGZpbmQgdGhlIGltcG9ydGFudCB3b3JkcyBmb3IgdGhlIGNvbnRlbnQgb2YgZWFjaCBkb2N1bWVudCBieSBkZWNyZWFzaW5nIHRoZSB3ZWlnaHQgZm9yIGNvbW1vbmx5IHVzZWQgd29yZHMgYW5kIGluY3JlYXNpbmcgdGhlIHdlaWdodCBmb3Igd29yZHMgdGhhdCBhcmUgbm90IHVzZWQgdmVyeSBtdWNoIGluIGEgY29sbGVjdGlvbiBvciBjb3JwdXMgb2YgZG9jdW1lbnRzLgoKV2Ugc2hvdyBiZWxvdyB0aGUgdG9wIHRmLWlkZiBzY29yZSB3b3JkcyBpbiB0aGUgc3BlZWNoLgpgYGB7cn0KIyB0Zi1pZGYgZm9yIGRlbW9jcmF0aWMKZHRtX2RlbW8gPC0gRG9jdW1lbnRUZXJtTWF0cml4KGNvcnB1c19kZW1vLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KHdlaWdodGluZyA9IGZ1bmN0aW9uKHgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodFRmSWRmKHgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemUgPUZBTFNFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wd29yZHMgPSBUUlVFKSkKdGZfaWRmX2RlbW8gPC0gdGlkeShkdG1fZGVtbykKcHJpbnQoaGVhZChhcnJhbmdlKHRmX2lkZl9kZW1vLGRlc2MoY291bnQpKSwxMCkpCgojIHRmLWlkZiBmb3IgcmVwdWJsaWNhbgpkdG1fcmVwdWIgPC0gRG9jdW1lbnRUZXJtTWF0cml4KGNvcnB1c19yZXB1YiwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gbGlzdCh3ZWlnaHRpbmcgPSBmdW5jdGlvbih4KQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRUZklkZih4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybWFsaXplID1GQUxTRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RvcHdvcmRzID0gVFJVRSkpCnRmX2lkZl9yZXB1YiA8LSB0aWR5KGR0bV9yZXB1YikKcHJpbnQoaGVhZChhcnJhbmdlKHRmX2lkZl9yZXB1YixkZXNjKGNvdW50KSksMTApKQpgYGAKCgojIFN0ZXAgNTogU2VudGltZW50IEFuYWx5c2lzCgojIyBFbW90aW9uClRoZXJlIGFyZSBhIHZhcmlldHkgb2YgbWV0aG9kcyBhbmQgZGljdGlvbmFyaWVzIHRoYXQgZXhpc3QgZm9yIGV2YWx1YXRpbmcgdGhlIG9waW5pb24gb3IgZW1vdGlvbiBpbiB0ZXh0LiBUaGUgdGlkeXRleHQgcGFja2FnZSBjb250YWlucyBzZXZlcmFsIHNlbnRpbWVudCBsZXhpY29ucyBpbiB0aGUgc2VudGltZW50cyBkYXRhc2V0LiBUaGVyZSBhcmUgKm5yYyosICpiaW5nKiwgYW5kICpBRklOTiogbGV4aWNvbiwgaGVyZSB3ZSB1c2UgW05SQyBzZW50aW1lbnQgbGV4aW9uXShodHRwOi8vc2FpZm1vaGFtbWFkLmNvbS9XZWJQYWdlcy9OUkMtRW1vdGlvbi1MZXhpY29uLmh0bSkuLCB3aGljaCBjYXRlZ29yaXplcyB3b3JkcyBpbiBhIGJpbmFyeSBmYXNoaW9uICjigJx5ZXPigJ0v4oCcbm/igJ0pIGludG8gY2F0ZWdvcmllcyBvZiBwb3NpdGl2ZSwgbmVnYXRpdmUsIGFuZ2VyLCBhbnRpY2lwYXRpb24sIGRpc2d1c3QsIGZlYXIsIGpveSwgc2FkbmVzcywgc3VycHJpc2UsIGFuZCB0cnVzdC4gTW9yZSBpbmZvcm1hdGlvbiBjb3VsZCBiZSBmb3VuZCBbaGVyZV0oaHR0cDovL3RpZHl0ZXh0bWluaW5nLmNvbS9zZW50aW1lbnQuaHRtbCkuCgpgYGB7cn0KIyBjYWxjdWxhdGUgbWVhbnMgZm9yIGVhY2ggZW1vdGlvbgplbW8ubWVhbnNfZGVtbz1jb2xNZWFucyhzZWxlY3QoZmlsdGVyKHNlbnRlbmNlLmxpc3QsUGFydHk9PSdEZW1vY3JhdGljJyksIGFuZ2VyOnRydXN0KT4wLjAxKQplbW8ubWVhbnNfcmVwdWI9Y29sTWVhbnMoc2VsZWN0KGZpbHRlcihzZW50ZW5jZS5saXN0LFBhcnR5PT0nUmVwdWJsaWNhbicpLCBhbmdlcjp0cnVzdCk+MC4wMSkKCiMgdXNlIGJhcnBsb3QgdG8gc2hvdyBlbW90aW9uIGZvciB0aGUgdHdvIHBhcnRpZXMKY29sb3JfdXNlIDwtIGMoJ2Rhcmtnb2xkZW5yb2QxJywnY2hvY29sYXRlMScsJ2NvcmFsMScsJ2RvZGdlcmJsdWUzJywncmVkJywnY2FkZXRibHVlMycsJ2dvbGQnLCdncmVlbjMnKQoKYyA8LSBnZ3Bsb3QoZGF0YS5mcmFtZShlbW90aW9uPW5hbWVzKGVtby5tZWFuc19kZW1vKSxudW1iZXI9ZW1vLm1lYW5zX2RlbW8pLGFlcyhyZW9yZGVyKGVtb3Rpb24sbnVtYmVyKSxudW1iZXIpKSsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsZmlsbD1yZXYoY29sb3JfdXNlKSkrCiAgeGxhYignRW1vdGlvbicpKwogIGdndGl0bGUoJ0RlbW9jcmF0aWMnKSsKICBzY2FsZV95X3JldmVyc2UoKSsKICBjb29yZF9mbGlwKCkrCiAgdGhlbWVfZ3JleSgpCgoKZCA8LSBnZ3Bsb3QoZGF0YS5mcmFtZShlbW90aW9uPW5hbWVzKGVtby5tZWFuc19yZXB1YiksbnVtYmVyPWVtby5tZWFuc19yZXB1YiksYWVzKHJlb3JkZXIoZW1vdGlvbixudW1iZXIpLG51bWJlcikpKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JyxmaWxsPXJldihjb2xvcl91c2UpKSsKICB4bGFiKCcnKSsKICBnZ3RpdGxlKCdSZXB1YmxpY2FuJykrCiAgY29vcmRfZmxpcCgpKwogIHRoZW1lX2dyZXkoKQogIAogIAoKZ3JpZC5hcnJhbmdlKGMsIGQsIG5yb3c9MSwgbmNvbD0yKQoKYGBgCgpXZSBjb3VsZCBzZWUgc2ltaWxhcml0aWVzIG9mIHRoZSB0d28gcGFydGllcy4gVG9wIGVtb3Rpb25zIGFyZSBib3RoICd0cnVzdCcsICdhbnRpY2lwYXRpb24nLCBhbmQgJ2pveScsIHdoaWNoIGFyZSBhbGwgcG9zaXRpdmUgZW1vdGlvbnMuIFRoaXMgaXMgaW50dWl0aXZlIGJlY2F1c2UgaW5hdWd1cmF0aW9uIHNwZWVjaCBpcyBtZWFudCB0byBpbnNwaXJlIHBlb3BsZS4KCgojIyBQb3NpdGl2ZSAmIE5lZ2F0aXZlCgpXZSBjb3VsZCBhbHNvIHNlZSB0aGUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHNlbnRpbWVudCBjaGFuZ2UgdGhyb3VnaG91dCBzcGVlY2hzIG9mIGVhY2ggcHJlc2lkZW50LgpgYGB7cixtZXNzYWdlPUZBTFNFfQojIGhlcmUgd2Ugc2VsZWN0IGZvdXIgcHJlc2lkZW50cyB0byBkaXNjdXNzCnNlbGVjdF9wcmVzaWRlbnQgPC0gYygnSm9obiBGLiBLZW5uZWR5JywgJ0JhcmFjayBPYmFtYScsJ0dlb3JnZSBCdXNoJywgJ0RvbmFsZCBKLiBUcnVtcCcpCnNlbnRpbWVudCA8LSBmaWx0ZXIod29yZHMubGlzdCxQcmVzaWRlbnQlaW4lc2VsZWN0X3ByZXNpZGVudCkgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpKSAlPiUKICBjb3VudChGaWxlLCBpbmRleCA9IHNlbnQuaWQsIHNlbnRpbWVudCkgJT4lCiAgc3ByZWFkKHNlbnRpbWVudCwgbiwgZmlsbCA9IDApICU+JQogIG11dGF0ZShzZW50aW1lbnQgPSBwb3NpdGl2ZSAtIG5lZ2F0aXZlKQoKZ2dwbG90KHNlbnRpbWVudCwgYWVzKGluZGV4LCBzZW50aW1lbnQsIGZpbGwgPSBGaWxlKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH5GaWxlLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWVfeCIpKwogIHRoZW1lX2dyYXkoKQoKYGBgCgpCYXJhY2sgT2JhbWEgYW5kIEpvaG4gRi4gS2VubmVkeSByZXByZXNlbnQgRGVtb2NyYXRpYywgd2hpbGUgRG9uYWxkIEouIFRydW1wIGFuZCBHZW9yZ2UgQnVzaCByZXByZXNlbnQgUmVwdWJsaWNhbi4gV2UgY291bGQgc2VlIHRoYXQgRGVtb2NyYXRpYyBzZWVtIHRvIGhhdmUgYSBuZWdhdGl2ZSBwYXJ0IGFmdGVyIHRoZSBiZWdpbm5pbmcgb2YgdGhlIHNwZWVjaCwgd2hpbGUgUmVwdWJsaWNhbiBzZWVtIHRvIGhhdmUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHBhcnQgdGhyb3VnaG91dCB0aGUgc3BlZWNoLg==